from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin  # 提供用户认证所需的默认方法（如 is_authenticated）
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash  # 密码哈希处理
import logging
from config import Config  # 导入配置文件（包含数据库连接等配置）
import pymysql  # MySQL 连接驱动
from sqlalchemy import inspect  # 用于检查数据库表是否存在

# 配置日志（获取名为 __name__ 的日志器，通常为当前模块名）
logger = logging.getLogger(__name__)

# 初始化 SQLAlchemy 数据库对象
db = SQLAlchemy()


# ---------------------------
# 用户模型（继承自 UserMixin 和 db.Model）
# ---------------------------
class User(UserMixin, db.Model):
    """用户模型类，对应数据库中的 users 表"""
    __tablename__ = 'users'  # 数据库表名

    # 主键，自增整数，唯一标识用户
    id = db.Column(db.Integer, primary_key=True)

    # 用户名，唯一且不为空，用于登录认证
    username = db.Column(db.String(80), unique=True, nullable=False)

    # 密码哈希值，使用 Werkzeug 进行加密处理，不存储明文密码
    password_hash = db.Column(db.String(256))

    # 用户角色，'user' 为普通用户，'admin' 为管理员，默认值为普通用户
    role = db.Column(db.String(20), default='user')

    # 可选字段：用户扩展信息
    email = db.Column(db.String(120))         # 电子邮箱
    school = db.Column(db.String(100))        # 所属学校
    department = db.Column(db.String(100))    # 所属学院
    student_id = db.Column(db.String(50))     # 学号（学生用户）
    phone = db.Column(db.String(20))          # 联系电话

    # 创建时间，默认值为当前 UTC 时间（数据库存储时区统一管理）
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    # 与手势记录模型的关联关系：
    # - backref='user'：在 GestureRecord 中可通过 user 属性反向引用 User 对象
    # - lazy=True：默认使用懒加载（查询时按需加载关联数据）
    # - cascade='all, delete-orphan'：级联操作（删除用户时自动删除关联记录）
    records = db.relationship('GestureRecord', backref='user', lazy=True, cascade='all, delete-orphan')

    # ---------------------------
    # 密码相关方法（遵循 Flask-Login 认证规范）
    # ---------------------------
    def set_password(self, password):
        """
        设置用户密码：
        对输入的明文密码进行哈希处理（使用 Werkzeug 的安全哈希算法），
        结果存储到 password_hash 字段
        """
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        """
        验证用户密码：
        将输入的明文密码与存储的哈希值进行比对，
        返回布尔值表示验证结果
        """
        return check_password_hash(self.password_hash, password)

    def get_id(self):
        """
        重写 Flask-Login 要求的 get_id 方法：
        返回用户 ID 的字符串表示，确保兼容不同数据库类型
        """
        return str(self.id)


# ---------------------------
# 手势记录模型
# ---------------------------
class GestureRecord(db.Model):
    """手势识别记录模型类，对应数据库中的 gesture_records 表"""
    __tablename__ = 'gesture_records'  # 数据库表名

    id = db.Column(db.Integer, primary_key=True)  # 主键，自增整数

    # 用户 ID，外键关联到 users 表的 id 字段，允许为 null（支持未登录用户的临时记录）
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True)

    # 识别的手势标识（如 '1', '2', '5' 等预定义手势），不能为空
    gesture = db.Column(db.String(10), nullable=False)

    # 手势对应的内容（用户自定义指令，如打开应用、发送消息等）
    content = db.Column(db.String(200))

    # 记录时间，默认值为当前 UTC 时间，用于审计和统计
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)


# ---------------------------
# 数据库操作函数（初始化和管理）
# ---------------------------
def create_database():
    """
    手动创建 MySQL 数据库及用户（非通过 ORM，直接执行原生 SQL）：
    1. 创建数据库（指定字符集支持 emoji 等特殊字符）
    2. 创建数据库用户并授权（支持远程连接）
    """
    try:
        # 连接到 MySQL 服务器（不指定具体数据库，仅操作服务器级别对象）
        conn = pymysql.connect(
            host=Config.MYSQL_HOST,     # 数据库主机地址（如本地或云端数据库地址）
            user=Config.MYSQL_USER,     # 数据库管理员用户名（用于创建数据库和用户）
            password=Config.MYSQL_PASSWORD,  # 管理员密码
            charset='utf8mb4'           # 字符集，确保支持 Unicode 全范围字符
        )

        with conn.cursor() as cursor:
            # 1. 创建数据库（如果不存在）
            cursor.execute(
                f"CREATE DATABASE IF NOT EXISTS `{Config.MYSQL_DB}` "
                "CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
            )
            logger.info(f"数据库 {Config.MYSQL_DB} 创建成功")

            # 2. 创建应用数据库用户并授权（适用于 MySQL 8.0+）
            try:
                # 创建用户（% 表示允许从任意 IP 地址连接）
                cursor.execute(
                    f"CREATE USER IF NOT EXISTS '{Config.MYSQL_USER}'@'%' "
                    f"IDENTIFIED BY '{Config.MYSQL_PASSWORD}'"
                )
                # 授予该用户对指定数据库的所有操作权限
                cursor.execute(
                    f"GRANT ALL PRIVILEGES ON `{Config.MYSQL_DB}`.* "
                    f"TO '{Config.MYSQL_USER}'@'%'"
                )
                # 刷新权限表使配置生效
                cursor.execute("FLUSH PRIVILEGES")
                logger.info(f"数据库用户 {Config.MYSQL_USER} 创建并授权完成")
            except pymysql.Error as e:
                # 忽略用户已存在等非致命错误（如重复初始化）
                logger.warning(f"用户创建/授权时出错: {str(e)}，可能已存在")

        # 显式提交事务（pymysql 默认不自动提交）
        conn.commit()
        return True
    except Exception as e:
        # 捕获连接失败、SQL 语法错误等致命问题
        logger.error(f"数据库创建失败: {str(e)}")
        return False
    finally:
        # 确保关闭数据库连接，释放资源
        if 'conn' in locals() and conn:
            conn.close()


def create_default_users():
    """
    创建默认初始化用户（用于开发和测试环境）：
    - 管理员用户（admin）：拥有最高权限
    - 普通测试用户（test）：用于功能测试
    """
    try:
        # ---------------------------
        # 创建管理员用户
        # ---------------------------
        admin = User(
            username='admin',          # 固定用户名（建议生产环境修改）
            role='admin',              # 角色标识为管理员
            email='admin@example.com',  # 示例邮箱（可替换为实际邮箱）
            school='西北工业大学',       # 示例学校信息
            department='软件学院',       # 示例学院信息
            student_id='2022222222',    # 示例学号
            phone='13800138000'         # 示例电话
        )
        admin.set_password('admin123')  # 设置密码（生产环境需使用强密码）
        db.session.add(admin)           # 添加到数据库会话

        # ---------------------------
        # 创建普通测试用户
        # ---------------------------
        test_user = User(
            username='test',            # 测试用户名
            role='user',                # 普通用户角色
            email='test@example.com',    # 测试邮箱
            school='西北工业大学',         # 与管理员相同的学校信息
            department='软件学院',         # 与管理员相同的学院信息
            student_id='2024222222',     # 不同的学号
            phone='13800138001'          # 不同的电话
        )
        test_user.set_password('test123')  # 设置测试密码
        db.session.add(test_user)          # 添加到数据库会话

        # 提交会话，将两个用户写入数据库
        db.session.commit()
        logger.info("默认用户（admin 和 test）创建完成")
        return True
    except Exception as e:
        # 发生错误时回滚事务，避免部分数据残留
        db.session.rollback()
        logger.error(f"默认用户创建失败: {str(e)}")
        return False


def init_db(app):
    """
    初始化数据库的完整流程（建议在 Flask 应用启动时调用）：
    1. 创建数据库和用户（通过原生 SQL）
    2. 创建数据库表（通过 SQLAlchemy ORM）
    3. 添加默认用户（如果用户表为空）
    """
    db.init_app(app)  # 将数据库对象绑定到 Flask 应用

    with app.app_context():  # 激活应用上下文，确保能访问配置和数据库
        # ---------------------------
        # 第一步：创建数据库和用户
        # ---------------------------
        if not create_database():
            raise RuntimeError("数据库初始化失败，无法继续")  # 终止初始化流程

        # ---------------------------
        # 第二步：检查数据库表结构
        # ---------------------------
        inspector = inspect(db.engine)  # 获取数据库结构检查器
        # 检查用户表是否存在（核心表，判断数据库是否已初始化）
        if not inspector.has_table('users'):
            # 通过 ORM 创建所有表（根据模型类自动生成表结构）
            db.create_all()
            logger.info("数据库表结构创建完成")

            # ---------------------------
            # 第三步：初始化默认用户
            # ---------------------------
            if User.query.count() == 0:  # 如果用户表为空
                create_default_users()    # 创建管理员和测试用户
        else:
            logger.info("数据库表结构已存在，跳过创建")